#!/usr/bin/env node

/*
 * jwtverify - verify JWT/JWS file or string
 *
 * Copyright (c) 2015-2016 Kenji Urushima (kenji.urushima@gmail.com)
 *
 * This software is licensed under the terms of the MIT License.
 * https://kjur.github.io/jsrsasign/license
 *
 * The above copyright and license notice shall be 
 * included in all copies or substantial portions of the Software.
 * 
 * Please use '-h' option for this script usage.
 * ---------------------------------------------------------
 * DESCRIPTION
 *   This script verifies a JWT(JSON Web Token) file.
 *
 * USAGE
 *   % jwtverify a1.jwt -k pub.pem
 *   This JWT/JWS is valid.
 *   % jwtverify a1.jwt -k pub.pem -v # verbose mode
 *   *** HEADER ***
 *   {
 *     "alg": "ES256",
 *     "cty": "JWT"
 *   }
 *   *** PAYLOAD ***
 *   {
 *     "age": 21
 *   }
 *   *** JWT/JWS VALIDATION RESULT ***
 *     - on: JWS signature validation
 *     - on: check acceptable signature algorithm
 *     - on: verify at current time
 *   This JWT/JWS is valid.
 *
 *   % jwtverify a2.jwt -p secret -t utf8 # for HS256 at jwt.io
 *   % jwtverify a3.jwt -v --verifyat 20050101000000Z -p secret
 *   % jwtverify a4.jwt -v --accept_iss http://aaa.com/ -p secret
 *   % jwtverify a5.jwt -v --accept_sub mailto:kj@aaa.com -p secret
 */

var program = require('commander');
var rs = require('jsrsasign');
var rsu = require('jsrsasign-util');
var path = require('path');
var JWS = rs.jws.JWS;

program
  .version('1.0.2 (2016-Nov-05)')
  .usage('[options] <JWT/JWS file or string to verify>')
  .description('verify JWT/jWS file or string')
  .option('-t, --passtype <utf8|hex|b64|b64u>', 'Hmac(HS*) pass type', 'utf8')
  .option('-p, --pass <pass>', 'Hmac(HS*) password in specfied type', 'passwd')
  .option('-k, --pubkey <file>', 'public key file (ex. PKCS#8 PEM or JWK)')
  .option('-v, --verbose', 'show header and payload')
  .option('--accept_iss <iss1,...>', 'check iss is in the iss list (ex. a@a.com,b@b.com)')
  .option('--accept_sub <sub1,...>', 'check sub is in the sub list (ex. a@a.com,b@b.com)')
  .option('--verify_at <YYYYMMDDHHmmSSZ>', 'verify at specified UTC time(ex. 20151123235959Z)')
  .parse(process.argv);

if (program.args.length !== 1)
  throw "wrong number of arguments";

var jwt;
try {
  var inFile  = program.args[0];
  jwt = rsu.readFile(inFile);
} catch(ex) {
  jwt = program.args[0]; // as string
}
//console.log(jwt);

var pass;
var pubKeyObj;
var acceptField = {};

if (! JWS.inArray(program.passtype, ['utf8', 'hex', 'b64', 'b64u']))
  throw "unsupported HS* password type: " + program.passtype;
if (program.passtype !== undefined && program.pass !== undefined) {
  pass = {};
  pass[program.passtype] = program.pass;
}

if (program.pubkey !== undefined) {
   var pubKeyPEM = rsu.readFile(program.pubkey);
   pubKeyObj = rs.KEYUTIL.getKey(pubKeyPEM);
}

/*
 * set acceptField
 */
if (pass !== undefined) 
  acceptField.alg = ['HS256', 'HS384', 'HS512'];
if (pubKeyObj !== undefined) 
  acceptField.alg = ['RS256', 'RS384', 'RS512',
                     'PS256', 'PS384', 'PS512',
                     'ES256', 'ES384', 'ES512'];

if (program.verify_at !== undefined)
  acceptField.verifyAt = rs.KJUR.jws.IntDate.getZulu(program.verify_at);

if (program.accept_iss !== undefined)
  acceptField.iss = program.accept_iss.split(",");

if (program.accept_sub !== undefined)
  acceptField.sub = program.accept_sub.split(",");

/*
 * show header and payload
 */
if (program.verbose) {
  var a = jwt.split(".");
  var pHeader = rs.KJUR.jws.JWS.readSafeJSONString(rs.b64utoutf8(a[0]));
  var pClaim = rs.KJUR.jws.JWS.readSafeJSONString(rs.b64utoutf8(a[1]));
  var sHeader = JSON.stringify(pHeader, null, "  ");
  var sClaim = JSON.stringify(pClaim, null, "  ");
  console.log("*** HEADER ***");
  console.log(sHeader);
  console.log("*** PAYLOAD ***");
  console.log(sClaim);
}

/*
 * show result
 */
if (program.verbose)
  console.log("*** JWT/JWS VALIDATION RESULT ***");

var isValid;

if (pubKeyObj !== undefined) {
   //console.log("pubKeyObj defined");
   isValid = rs.jws.JWS.verifyJWT(jwt, pubKeyObj, acceptField);
} else {
   //console.log("pass defined");
   isValid = rs.jws.JWS.verifyJWT(jwt, pass, acceptField);
}

if (program.verbose) {
  console.log('  - on: JWS signature validation');
  if (acceptField.alg !== undefined)
    console.log('  - on: check acceptable signature algorithm');
  if (program.verify_at === undefined) {
    console.log('  - on: verify at current time');
  } else {
    console.log('  - on: verify at %j', program.verify_at);
  }

  if (acceptField.iss != undefined)
    console.log('  - on: check iss in %j', program.accept_iss);

  if (acceptField.sub != undefined)
    console.log('  - on: check sub in %j', program.accept_sub);
}

if (isValid) {
  console.log('This JWT/JWS is valid.');
} else {
  console.log('This JWT/JWS is *NOT* valid.');
}
